using System;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

using SensorShell;
using VisualStudioSensor;
using TeamFoundationServerSensor;

/// <summary>
/// Summary description for SensorShellControl
/// </summary>
public class SensorShellControl {

    /// <summary>
    /// The tool that we are sensing data from. AlphaNumeric only without spaces.
    /// e.g. VisualStudio, VSTeamFoundationServer, etc.
    /// </summary>
    private string tool;

    /// <summary>
    /// The location of the sensorshell can be specified if applicable.
    /// </summary>
    private string sensorshellPath = null;

    /// <summary>
    /// Constructor for creating a control wrapper for sensor connections
    /// that defaults tool to "VisualStudio" for compatability.
    /// </summary>
    public SensorShellControl() {
        this.tool = "VisualStudio";
    }

    /// <summary>
    /// Constructor for creating a control wrapper for sensor connections.
    /// </summary>
    /// <param name="tool">The name of the tool making the connection.</param>
    public SensorShellControl(string tool) {
        this.tool = tool;
    }

    /// <summary>
    /// Constructor overload which takes in a path to the sensorshell jar file
    /// incase it needs to be manually set.
    /// </summary>
    /// <param name="tool">The name of the tool that is being used.</param>
    /// <param name="sensorshellPath">The path to the sensorshell</param>
    public SensorShellControl(string tool, string sensorshellPath) {
        this.tool = tool;
        this.sensorshellPath = sensorshellPath;
    }

    /// <summary>
    /// Attempts to create a connection with the Hackystat server.
    /// </summary>
    /// <returns>True if the sensorshell could be started.</returns>
    public Boolean Connect() {

        // Start the sensor connection with Hackystat
        SensorShellProperties shellProperties = new SensorShellProperties();

        // Get the locations of the java installation and sensorshell
        // installation.
        string javaPath = getPathJavaHome();
        string sensorPath = getPathSensorShell();
        if (javaPath == null) {
            throw new DirectoryNotFoundException("Cannot find the Java directory.");
        }
        else if (sensorPath == null) {
            throw new DirectoryNotFoundException("Cannot find the SensorShell directory.");
        }
        else {
            Logger.TheInstance.Log("Trying to start java sensorshell sub-process using: ",
                LogLevel.Info);
            Logger.TheInstance.Log("    JavaHome = " + javaPath, LogLevel.Info);
            Logger.TheInstance.Log("    SensorShell = " + sensorPath, LogLevel.Info);

            // Determine if the sensor shell was started correctly.
            if (!SensorShellProxy.TheInstance.status() && SensorShellProxy.TheInstance.Start(tool, javaPath, sensorPath)) {
                Logger.TheInstance.Log("Hackystat sensor started successfully.",
                    LogLevel.Info);
                return true;
            }
            else {
                Logger.TheInstance.Log("Sensorshell already started.",
                    LogLevel.Info);
                return true;
            }
        }
    }

    /// <summary>
    /// Attempts to discover the java home directory. Returns null if no
    /// directory can be found.
    /// </summary>
    /// <returns>A string of the java home directory.</returns>
    private string getPathJavaHome() {

        string validJavaHome = null;

        // Attempt to read the java home directory from the registry.
        string javaHome = WindowsRegistryUtil.ReadProperty(WindowsRegistryUtil.JAVA_HOME);
        if (javaHome == null) {
            Logger.TheInstance.Log("Java home not set. Trying to find one in Windows registry.",
                LogLevel.Info);
            javaHome = JavaUtil.FindJavaHomePath(SensorShell.SensorShell.MIN_JAVA_VERSION_MAJOR,
                SensorShell.SensorShell.MIN_JAVA_VERSION_MINOR);
            if (javaHome == null) {
                Logger.TheInstance.Log("Unable to find a suitable java home. "
                    + "Please config the sensor using configuration tool on the"
                    + " Tools menu.", LogLevel.Info);
            }
            else {
                Logger.TheInstance.Log("Using Java_Home at " + javaHome, LogLevel.Info);
            }
        }
        try {
            if (javaHome != null) {
                JavaUtil.VerifyJavaHome(javaHome);
                validJavaHome = javaHome;
            }
        }
        catch (SensorShellException ex) {
            Logger.TheInstance.Log(ex);
        }


        return validJavaHome;
    }

    /// <summary>
    /// Attempts to discover the path to the sensorshell jar files required to
    /// communicate with the hackystat server. Returns null if no
    /// directory can be found.
    /// </summary>
    /// <returns>A string path to the directory.</returns>
    private string getPathSensorShell() {

        // Has a path been specified. If so attempt to use it.
        if ((sensorshellPath != null || sensorshellPath.Equals("")) && File.Exists(sensorshellPath)) {
            Logger.TheInstance.Log("Using Custom SensorShell.jar at "
                + sensorshellPath, LogLevel.Info);
        }

        // Attempt to look for sensorshell
        else {
            string currentDirectory = this.GetRunningDirectory();

            Logger.TheInstance.Log("Jar file sensorshell.jar not set. " +
              "Trying to find one in this directory: " + currentDirectory, LogLevel.Info);
            string tmpSensorShellPath = currentDirectory + @"\sensorshell.jar";

            if (File.Exists(tmpSensorShellPath)) {
                sensorshellPath = tmpSensorShellPath;
                Logger.TheInstance.Log("Found possible SensorShell.jar at " + sensorshellPath, LogLevel.Info);
            }
            else {
                sensorshellPath = WindowsRegistryUtil.ReadProperty(WindowsRegistryUtil.SENSOR_SHELL_JAR);
            }
            try {
                if (sensorshellPath != null) {
                    JavaUtil.VerifySensorShellJar(sensorshellPath);
                    Logger.TheInstance.Log("Using SensorShell.jar at " + sensorshellPath, LogLevel.Info);
                }
            }
            catch (SensorShellException ex) {
                Logger.TheInstance.Log(ex);
                sensorshellPath = null;
            }
        }

        return sensorshellPath;
    }

    /// <summary>
    /// Gets the fully-qualified directory where this assembly locates. By default, sensorshell.jar
    /// is put in this directory.
    /// </summary>
    /// <returns>The directory path.</returns>
    private string GetRunningDirectory() {
        string assemblyLocation = Assembly.GetExecutingAssembly().Location;
        string directory = new FileInfo(assemblyLocation).DirectoryName;
        return directory;
    }

    /// <summary>
    /// Used to return the name of the tool that is sending data.
    /// </summary>
    /// <returns>String representing the tool name.</returns>
    public string getTool() {
        return tool;
    }

    /// <summary>
    /// Sets the tool name that is sending data.
    /// </summary>
    /// <param name="tool">Name of the tool being used.</param>
    public void setTool(string tool) {
        // Make sure that the text is alpha-numeric only. 
        if (!Regex.IsMatch(tool, @"^[a-zA-Z0-9]+$")) {
            throw new ArgumentException("Tool name must be strictly alpha-numeric.");
        }
        else {
            this.tool = tool;
        }
    }

    /// <summary>
    /// Get the status of the sensorshell.
    /// </summary>
    /// <returns></returns>
    public static Boolean running() {
        return SensorShellProxy.TheInstance.status();
    }
}
